#ifndef __IM_IO_CHANNEL_H__
#define __IM_IO_CHANNEL_H__

#include <im_base_types.h>
#include <im_object.h>

/**
 * @defgroup IMIOChannel
 * @ingroup PrivateIMBusIME
 * @{
 */

/** @struct IMIOChannel
 *  @brief It's an interface class for real iochannel implementation.
 *
 *  The purpose of IMIOChannel is to abstract all operations of lower
 *  level I/O facilities, such as file, socket, pipe, etc.
 */

IM_BEGIN_DECLS

#define IM_IO_CHANNEL(p)             (IM_TYPE_INSTANCE_CHECK_CAST((p),IM_TYPE_IO_CHANNEL,IMIOChannel))
#define IM_CONST_IO_CHANNEL(p)       (IM_TYPE_INSTANCE_CHECK_CAST_CONST((p),IM_TYPE_IO_CHANNEL,IMIOChannel))
#define IM_IO_CHANNEL_CLASS(c)       (IM_TYPE_CLASS_CHECK_CAST((c),IM_TYPE_IO_CHANNEL,IMIOChannelClass))
#define IM_IS_IO_CHANNEL(p)          (IM_TYPE_INSTANCE_CHECK_TYPE((p),IM_TYPE_IO_CHANNEL))
#define IM_IS_IO_CHANNEL_CLASS(c)    (IM_TYPE_CLASS_CHECK_TYPE((c),IM_TYPE_IO_CHANNEL))
#define IM_IO_CHANNEL_GET_CLASS(p)   (IM_TYPE_INSTANCE_GET_CLASS_CAST((p),IM_TYPE_IO_CHANNEL,IMIOChannelClass))

typedef enum
{
    IM_IO_ERROR_NONE = 0,

    /* Derived from errno */
    IM_IO_ERROR_ACCES,      /* Permission denied */
    IM_IO_ERROR_AGAIN,      /* Resource temporarily unavailable */
    IM_IO_ERROR_BADF,       /* Bad file descriptor */
    IM_IO_ERROR_FAULT,      /* Bad address */
    IM_IO_ERROR_FBIG,       /* File too large */
    IM_IO_ERROR_INTR,       /* Interrupted function call */
    IM_IO_ERROR_INVAL,      /* Invalid argument */
    IM_IO_ERROR_IO,         /* Input/output error */
    IM_IO_ERROR_ISDIR,      /* Is a directory */
    IM_IO_ERROR_NFILE,      /* Too many open files in system */
    IM_IO_ERROR_NODEV,      /* No such device */
    IM_IO_ERROR_NOMEM,      /* Not enough space */
    IM_IO_ERROR_NOSPC,      /* No space left on device */
    IM_IO_ERROR_NXIO,       /* No such device or address */
    IM_IO_ERROR_OVERFLOW,   /* Value too large to be stored in data type */
    IM_IO_ERROR_PIPE,       /* Broken pipe */
    IM_IO_ERROR_SPIPE,      /* Invalid seek */
    /* Other */
    IM_IO_ERROR_FAILED
} IMIOError;

typedef enum
{
    IM_IO_SEEK_CUR,
    IM_IO_SEEK_SET,
    IM_IO_SEEK_END
} IMIOSeekType;

typedef enum
{
    IM_IO_FLAG_APPEND       = 1 << 0,
    IM_IO_FLAG_NONBLOCK     = 1 << 1,
    IM_IO_FLAG_IS_READABLE  = 1 << 2,   /* Read only flag */
    IM_IO_FLAG_IS_WRITEABLE = 1 << 3,   /* Read only flag */
    IM_IO_FLAG_IS_SEEKABLE  = 1 << 4,   /* Read only flag */
    IM_IO_FLAG_IS_MMAPABLE  = 1 << 5,   /* Read only flag */
    IM_IO_FLAG_MASK         = (1 << 6) - 1,
    IM_IO_FLAG_GET_MASK     = IM_IO_FLAG_MASK,
    IM_IO_FLAG_SET_MASK     = IM_IO_FLAG_APPEND | IM_IO_FLAG_NONBLOCK
} IMIOFlags;

typedef struct _IMIOChannel         IMIOChannel;
typedef struct _IMIOChannelClass    IMIOChannelClass;

struct _IMIOChannel
{
    IMInitiallyOwned parent_object;

    /* Private data */
    IMUInt close_on_unref : 1;
};

struct _IMIOChannelClass
{
    IMInitiallyOwnedClass parent_class;

    IMBool      (*valid)                (IMIOChannel    *channel);
    IMBool      (*read)                 (IMIOChannel    *channel,
                                         IMChar         *buf,
                                         IMSize          count,
                                         IMSize         *bytes_read,
                                         IMIOError      *error);
    IMBool      (*write)                (IMIOChannel    *channel,
                                         const IMChar   *buf,
                                         IMSize          count,
                                         IMSize         *bytes_written,
                                         IMIOError      *error);
    IMBool      (*seek)                 (IMIOChannel    *channel,
                                         IMInt64         offset,
                                         IMIOSeekType    type,
                                         IMIOError      *error);
    IMBool      (*tell)                 (IMIOChannel    *channel,
                                         IMUInt64       *offset,
                                         IMIOError      *error);
    IMBool      (*close)                (IMIOChannel    *channel,
                                         IMIOError      *error);
    IMPointer   (*mmap)                 (IMIOChannel    *channel,
                                         IMUInt64        offset,
                                         IMSize          length,
                                         IMBool          writeable,
                                         IMBool          shared,
                                         IMIOError      *error);
    IMBool      (*munmap)               (IMIOChannel    *channel,
                                         IMPointer       address,
                                         IMSize          length,
                                         IMIOError      *error);
    IMBool      (*set_flags)            (IMIOChannel    *channel,
                                         IMUInt          flags,
                                         IMIOError      *error);
    IMBool      (*get_flags)            (IMIOChannel    *channel,
                                         IMUInt         *flags,
                                         IMIOError      *error);

    /* Simple non-blocking read/write interface. */
    IMBool      (*wait_for_readable)    (IMIOChannel    *channel,
                                         IMInt          *timeout,
                                         IMIOError      *error);
    IMBool      (*read_with_timeout)    (IMIOChannel    *channel,
                                         IMChar         *buf,
                                         IMSize          count,
                                         IMSize         *bytes_read,
                                         IMInt          *timeout,
                                         IMIOError      *error);
    IMBool      (*wait_for_writeable)   (IMIOChannel    *channel,
                                         IMInt          *timeout,
                                         IMIOError      *error);
    IMBool      (*write_with_timeout)   (IMIOChannel    *channel,
                                         const IMChar   *buf,
                                         IMSize          count,
                                         IMSize         *bytes_written,
                                         IMInt          *timeout,
                                         IMIOError      *error);
};

IMBool          im_io_channel_valid         (IMIOChannel    *channel);

/**
 * @brief   Reads data from an IMIOChannel.
 *
 * @param   channel     an IMIOChannel
 * @param   buf         a buffer to read data into.
 * @param   count       the size of the buffer. Note that the buffer may not be
 *                      complelely filled even if there is data in the buffer
 *                      if the remaining data is not a complete character.
 * @param   bytes_read  the number of bytes read.  This may be zero even on
 *                      success if count < 6 and the channel's encoding is
 *                      non-NULL. This indicates that the next UTF-8 character
 *                      is too wide for the buffer.
 * @param   error       a location to return an error of type IMIOError.
 *
 * @returns TRUE if something is read.
 */
IMBool          im_io_channel_read          (IMIOChannel    *channel,
                                             IMChar         *buf,
                                             IMSize          count,
                                             IMSize         *bytes_read,
                                             IMIOError      *error);

/**
 * @brief   Writes data to an IMIOChannel.
 *
 * @param   channel     an IMIOChannel.
 * @param   buf         the buffer containing the data to write.
 * @param   count       the number of bytes to write.
 * @param   bytes_written  the number of bytes actually written.
 * @param   error       a location to return an error of type IMIOError.
 *
 * @returns TRUE if something is written.
 */
IMBool          im_io_channel_write         (IMIOChannel    *channel,
                                             const IMChar   *buf,
                                             IMSize          count,
                                             IMSize         *bytes_written,
                                             IMIOError      *error);

/**
 * @brief   Sets the current position in the GIOChannel, similar to the
 *          standard library function fseek().
 *
 * @param   channel     an IMIOChannel.
 * @param   offset      an offset, in bytes, which is added to the position
 *                      specified by type.
 * @param   type        the position in the file, which can be IM_IO_SEEK_CUR
 *                      (the current position), IM_IO_SEEK_SET (the start of
 *                      the file), or IM_IO_SEEK_END (the end of the file).
 * @param   error       a location to return an error of type IMIOError.
 *
 * @return  TRUE if the operation is successful.
 */
IMBool          im_io_channel_seek          (IMIOChannel    *channel,
                                             IMInt64         offset,
                                             IMIOSeekType    type,
                                             IMIOError      *error);

/**
 * @brief   Obtains the current position in the GIOChannel, similar to the
 *          standard library function ftell().
 *
 * @param   channel     an IMIOChannel.
 * @param   offset      a location to return the offset, in bytes.
 * @param   error       a location to return an error of type IMIOError.
 *
 * @return  TRUE if the operation is successful.
 */
IMBool          im_io_channel_tell          (IMIOChannel    *channel,
                                             IMUInt64       *offset,
                                             IMIOError      *error);

/**
 * @brief   Closes an IO channel.
 *
 * Any pending data to be written will be flushed, ignoring errors.
 *
 * @param   channel     an IMIOChannel.
 */
void            im_io_channel_close         (IMIOChannel    *channel);

/**
 * @brief   Maps an area in an IMIOChannel into memory.
 *
 * @param   channel     an IMIOChannel.
 * @param   offset      the offset in the channel of the area to map.
 * @param   length      the length of the area to map.
 * @param   writeable   TRUE if the mapped area can be written to.
 * @param   shared      TRUE if the mapped area is shared with all other
 *                      processes that map this area.
 * @param   error       a location to return an error of type IMIOError.
 *
 * @returns a pointer to the mapped area.
 */
IMPointer       im_io_channel_mmap          (IMIOChannel    *channel,
                                             IMUInt64        offset,
                                             IMSize          length,
                                             IMBool          writeable,
                                             IMBool          shared,
                                             IMIOError      *error);

/**
 * @brief   Unmaps a previously mapped area.
 *
 * @param   channel     an IMIOChannel.
 * @param   address     the pointer to the mapped area to unmap.
 * @param   length      the length of the area to unmap.
 * @param   error       a location to return an error of type IMIOError.
 *
 * @returns TRUE if the operation is successful.
 */
IMBool          im_io_channel_munmap        (IMIOChannel    *channel,
                                             IMPointer       address,
                                             IMSize          length,
                                             IMIOError      *error);

/**
 * @brief   Sets the flags for an IMIOChannel.
 *
 * @param   channel     an IMIOChannel.
 * @param   flags       the flags to set on the IO channel.
 * @param   error       a location to return an error of type IMIOError.
 *
 * @returns TRUE if the operation is successful.
 */
IMBool          im_io_channel_set_flags     (IMIOChannel    *channel,
                                             IMUInt          flags,
                                             IMIOError      *error);

/**
 * @brief   Gets the current flags for an IMIOChannel.
 *
 * @param   channel     an IMIOChannel.
 * @param   flags       a location to return the flags to set on the IO channel.
 * @param   error       a location to return an error of type IMIOError.
 *
 * @returns TRUE if the operation is successful.
 */
IMBool          im_io_channel_get_flags     (IMIOChannel    *channel,
                                             IMUInt         *flags,
                                             IMIOError      *error);

/**
 * @brief   Sets the append mode for an IMIOChannel.
 *
 * @param   channel     an IMIOChannel.
 * @param   append      TRUE to turn on append mode; corresponds to O_APPEND
 *                      (see the documentation of the UNIX open() syscall).
 * @param   error       a location to return an error of type IMIOError.
 *
 * @returns TRUE if the operation is successful.
 */
IMBool          im_io_channel_set_append    (IMIOChannel    *channel,
                                             IMBool          append,
                                             IMIOError      *error);

/**
 * @brief   Sets the non-blocking mode for an IMIOChannel.
 *
 * @param   channel     an IMIOChannel.
 * @param   nonblock    TRUE to turn on non-blocking mode; corresponds to
 *                      O_NONBLOCK/O_NDELAY
 *                      (see the documentation of the UNIX open() syscall).
 * @param   error       a location to return an error of type IMIOError.
 *
 * @returns TRUE if the operation is successful.
 */
IMBool          im_io_channel_set_nonblock  (IMIOChannel    *channel,
                                             IMBool          nonblock,
                                             IMIOError      *error);

/**
 * @brief   Checks whether an IMIOChannel is in append mode.
 * @param   channel     an IMIOChannel.
 * @returns TRUE if the IMIOChannel is in append mode.
 */
IMBool          im_io_channel_is_append     (IMIOChannel    *channel);
/**
 * @brief   Checks whether an IMIOChannel is in non-blocking mode.
 * @param   channel     an IMIOChannel.
 * @returns TRUE if the IMIOChannel is in non-blocking mode.
 */
IMBool          im_io_channel_is_nonblock   (IMIOChannel    *channel);
/**
 * @brief   Checks whether an IMIOChannel can be read from.
 * @param   channel     an IMIOChannel.
 * @returns TRUE if the IMIOChannel is readable.
 */
IMBool          im_io_channel_is_readable   (IMIOChannel    *channel);
/**
 * @brief   Checks whether an IMIOChannel can be written to.
 * @param   channel     an IMIOChannel.
 * @returns TRUE if the IMIOChannel is writeable.
 */
IMBool          im_io_channel_is_writeable  (IMIOChannel    *channel);
/**
 * @brief   Checks whether it is possible to seek in an IMIOChannel.
 * @param   channel     an IMIOChannel.
 * @returns TRUE if the IMIOChannel is seekable.
 */
IMBool          im_io_channel_is_seekable   (IMIOChannel    *channel);
/**
 * @brief   Checks whether it is possible to map an area of an IMIOChannel into
 *          memory.
 * @param   channel     an IMIOChannel.
 * @returns TRUE if the IMIOChannel is mmapable.
 */
IMBool          im_io_channel_is_mmapable   (IMIOChannel    *channel);

/**
 * @brief   Sets whether the IMIOChannel will be closed when channel is about
 *          to be destroyed.
 *
 * @param   channel     an IMIOChannel.
 * @param   do_close    whether to close the channel on the final unref of the
 *                      IMIOChannel data structure.
 */
void            im_io_channel_set_close_on_unref(IMIOChannel    *channel,
                                                 IMBool          do_close);

/**
 * @brief   Sets whether the IMIOChannel will be closed when channel is about
 *          to be destroyed.
 * @param   channel     an IMIOChannel.
 * @returns whether to close the channel on the final unref of the IMIOChannel
 *          data structure.
 */
IMBool          im_io_channel_get_close_on_unref(IMIOChannel    *channel);

/**
 * @brief   Converts an errno error number to an IMIOError.
 * @param   err     an errno error number, e.g. EINVAL.
 * @returns an IMIOError error number, e.g. IM_IO_ERROR_INVAL.
 */
IMIOError       im_io_channel_error_from_errno  (int             err);

/**
 * @brief   Creates an IMIOChannel object by opening or creating a file.
 *
 * @returns On success, the newly created IMIOChannel object will be returned
 *          with the initial reference count set to 1.
 */
IMIOChannel*    im_io_channel_new_file          (const IMChar   *filename,
                                                 const IMChar   *mode,
                                                 IMIOError      *error);

/**
 * @brief Wait until there are data can be read from a specific IO channel.
 *
 * @param channel The IO channel will be waited for.
 * @param timeout Maximum wait time in milliseconds, until
 *                there are some data in the io channel to be read.
 *                0 means immediately return the status without waiting.
 *                -1 means wait forever until data are available.
 *                If the function returns before the time is out, then
 *                the remained time will be stored in it.
 * @param error   Error number will be stored in it if any error is occurred.
 * @return True means the data is ready, otherwise return False.
 */
IMBool          im_io_channel_wait_for_readable (IMIOChannel    *channel,
                                                 IMInt          *timeout,
                                                 IMIOError      *error);
IMBool          im_io_channel_read_with_timeout (IMIOChannel    *channel,
                                                 IMChar         *buf,
                                                 IMSize          count,
                                                 IMSize         *bytes_read,
                                                 IMInt          *timeout,
                                                 IMIOError      *error);
/**
 * @brief Wait until data can be written to a specific IO channel.
 *
 * @param channel The IO channel will be waited for.
 * @param timeout Maximum wait time in milliseconds, until
 *                some data can be written into the io channel.
 *                0 means immediately return the status without waiting.
 *                -1 means wait forever until data can be written.
 *                If the function returns before the time is out, then
 *                the remained time will be stored in it.
 * @param error   Error number will be stored in it if any error is occurred.
 * @return True means the channel is ready to be written,
 *         otherwise return False.
 */
IMBool          im_io_channel_wait_for_writeable(IMIOChannel    *channel,
                                                 IMInt          *timeout,
                                                 IMIOError      *error);
IMBool          im_io_channel_write_with_timeout(IMIOChannel    *channel,
                                                 const IMChar   *buf,
                                                 IMSize          count,
                                                 IMSize         *bytes_written,
                                                 IMInt          *timeout,
                                                 IMIOError      *error);
IM_END_DECLS
/** @} */

#endif
/*
vi:ts=4:nowrap:ai:expandtab
*/
